import OverLay from 'ol/Overlay'
import { ColorDict, LineColorDict } from '@/types/index.d.ts'
import { Vector as VectorSource } from 'ol/source'
import { Vector as VectorLayer, Layer } from 'ol/layer'
import { Style, Circle, Stroke, Fill } from 'ol/style'
import Map from 'ol/Map'
import Feature from 'ol/Feature'
import { Geometry, LineString, Point, Polygon } from 'ol/geom'
import { fromLonLat } from 'ol/proj'
import GeoJSON from 'ol/format/GeoJSON'

// type TaiphoonData = typeof TaiphoonData

export default class Typhoon {
  private map: Map
  private data
  private forecastFeatures: Feature[]
  private _playFlag: NodeJS.Timeout | null
  private _playIndex: number
  private _typhoonName: OverLay | null
  private _vectorSource: VectorSource | null
  private _vectorLayer: VectorLayer<VectorSource> | null

  constructor(map: Map, data) {
    this.map = map
    this.data = data
    this.forecastFeatures = []
    this._playFlag = null
    this._playIndex = 0
    this._typhoonName = null
    this._vectorSource = null
    this._vectorLayer = null
    this._init()
  }
  _init() {
    // 第一步：创建图层，并初始化状态
    this._vectorSource = new VectorSource({
      features: []
    })
    this._vectorLayer = new VectorLayer({
      source: this._vectorSource!,
      style: this._styleFunction,
      properties: {
        name: '台风路径'
      }
    })
    this.map.addLayer(this._vectorLayer)
    this._addPopup()
    // 第二步：添加台风名称
    this._addTyphoonName()
    // 第三步：播放台风
    this._playTyphoon()
  }
  _readGeojson(data: any) {
    const d: Feature<Geometry> = new GeoJSON().readFeature(data) as Feature<Geometry>
    d.getGeometry()?.transform('EPSG:4326', 'EPSG:3857')
    return d
  }
  _addTyphoonName() {
    const ele = document.createElement('div')
    const { tfbh, name } = this.data[0]
    ele.innerHTML = tfbh + name
    const offset = [5, 0]
    const positioning = 'center-left'
    this._typhoonName = new OverLay({
      element: ele,
      positioning: positioning,
      offset: offset,
      className: 'typhoon-name',
      stopEvent: false
    })
    this.map.addOverlay(this._typhoonName)
    const { lng, lat } = this.data[0]
    const position = fromLonLat([lng, lat])
    this.map.getView().animate({
      center: fromLonLat([lng - 1, lat - 1]),
      zoom: 8
    })
    this._typhoonName.setPosition(position)
  }
  _playTyphoon() {
    if (this._playIndex === this.data.length) {
      clearTimeout(Number(this._playFlag))
    } else {
      this._playIndex++
      this._playTyphoonByIndex()
      this._playFlag = setTimeout(() => this._playTyphoon(), 500)
    }
  }
  // 一、添加实况展示
  _playTyphoonByIndex() {
    this._vectorSource?.clear()
    const features: Feature[] = []
    for (let i = 0; i < this._playIndex; i++) {
      // 1,加点
      const point = this.data[i]
      point.isLive = true   // 表明这个点不是预报点，是正在活动的点
      point.tyname = this.data[0].name
      const feature = this._readGeojson(point.point)
      feature.set('data', point)
      feature.setProperties({ name: '台风路径' })
      const coord = fromLonLat(point.point.coordinates)
      features.push(feature)

      // 2，加线
      if (i > 0) {
        const pointPrev = this.data[i - 1]
        const { lng: lngPrev, lat: latPrev } = pointPrev
        const coordPrev = fromLonLat([lngPrev, latPrev])
        const featureLine: Feature = new Feature({
          geometry: new LineString([coordPrev, coord]),
          data: point,
          properties: {
            name: '台风路径'
          }
        })
        features.push(featureLine)
      }
    }
    // 二、添加预报展示
    const forcasts = [JSON.parse(this.data[this._playIndex - 1].forecast)] || []
    // 添加预报数据
    forcasts.forEach((forcast: any) => {
      if (forcast !== null && !(forcast instanceof Array)) {
        const points = forcast.points

        points.map((p: any) => {
          p.sets = forcast.sets
        })

        points.forEach((point: any, index: number) => {
          point.isLive = false  // 表明这个点是预报点
          point.tyname = this.data[0].name
          point.time = point.time.split('T')[0] + ' ' + point.time.split('T')[1]
          // 添加预报点
          const { lng, lat } = point
          const coord = fromLonLat([lng, lat])
          const feature: Feature = new Feature({
            geometry: new Point(coord),
            data: point,
            properties: {
              name: '台风路径'
            }
          })
          this.forecastFeatures.push(feature)

          // 添加预报线
          const pointPrev = index === 0 ? this.data[this._playIndex - 1] : points[index - 1]
          const { lng: lngPrev, lat: latPrev } = pointPrev
          const coordPrev = fromLonLat([lngPrev, latPrev])
          const featureLine: Feature = new Feature({
            geometry: new LineString([coordPrev, coord]),
            data: point,
            properties: {
              name: '台风路径'
            }
          })
          this.forecastFeatures.push(featureLine)
        })
      }
    })
    features.push(...this.forecastFeatures)
    // 三、添加风圈展示
    const currentPoint = this.data[this._playIndex - 1]

    let radius7 = currentPoint['radius7Quad']
    let radius10 = currentPoint['radius10Quad']
    let radius12 = currentPoint['radius12Quad']

    const { lng, lat } = currentPoint
    const center = fromLonLat([lng, lat])
    const feat7 = this._getRadiusFeature(center, JSON.parse(radius7.value), 7)
    const feat10 = this._getRadiusFeature(center, JSON.parse(radius10.value), 10)
    const feat12 = this._getRadiusFeature(center, JSON.parse(radius12.value), 12)
    if (feat7) features.push(feat7)
    if (feat10) features.push(feat10)
    if (feat12) features.push(feat12)

    this._vectorSource?.addFeatures(features)
  }
  _getRadiusFeature(center: number[], radiusData: any, type: any) {
    if (!radiusData.ne || radiusData.ne === 0) {
      return new Feature({
        geometry: undefined,
        data: {
          radius: type
        }
      })
    } else {
      interface AngleDict {
        [key: string]: number[],
        'ne': number[],
        'se': number[],
        'sw': number[],
        'nw': number[]
      }
      const angleDict: AngleDict = {
        'ne': [0, 90],
        'se': [270, 360],
        'sw': [180, 270],
        'nw': [90, 180]
      }
      const coords = []
      for (const k in radiusData) {
        const r = radiusData[k]
        const minAngle = angleDict[k][0]
        const count = 18
        for (let i = 0; i <= count; i++) {
          const angle = minAngle + i * 5
          coords.push(this._getCoordsByAngle(center, r * 500, angle))
        }
      }
      const feature: Feature<Geometry> = new Feature({
        geometry: new Polygon([coords]),
        data: {
          radius: type,
          name: '风圈'
        }
      })
      return feature
    }
  }
  _getCoordsByAngle(center: number[], r: number, angle: number) {
    // 转换为弧度，r是半径
    angle = angle / 180 * Math.PI
    const x = center[0] + Math.cos(angle) * r
    const y = center[1] + Math.sin(angle) * r
    return [x, y]
  }
  // 添加弹出框
  _addPopup() {
    const container = document.createElement('div')
    container.className = 'popup'
    const popup = new OverLay({
      element: container,
      autoPan: true,
      positioning: 'bottom-center',
      stopEvent: false
    })
    this.map.addOverlay(popup)
    // 获取要素位置并定位弹出框
    this.map.on('singleclick', (e) => {
      const feature = this.map.forEachFeatureAtPixel(e.pixel, (feature, layer) => {
        if (layer.get('name') === '台风路径') return feature
      })
      if (feature && feature.getGeometry()?.getType() === 'Point') {
        const data = feature.get('data')
        console.log(data);

        const { tyname, time, lng, lat, speed, power, strong, pressure, move_dir } = data
        let radius7 = data['radius7Quad']
        let radius10 = data['radius10Quad']
        let radius12 = data['radius12Quad']
        const center = fromLonLat([lng, lat])
        const features = []
        const feat7 = this._getRadiusFeature(center, JSON.parse(radius7.value), 7)
        const feat10 = this._getRadiusFeature(center, JSON.parse(radius10.value), 10)
        const feat12 = this._getRadiusFeature(center, JSON.parse(radius12.value), 12)

        if (feat7) features.push(feat7)
        if (feat10) features.push(feat10)
        if (feat12) features.push(feat12)
        this.map.getAllLayers().forEach((layer: Layer) => {
          if (layer.get('name') === '台风路径') {
            let source: VectorSource = layer.getSource() as VectorSource
            source?.getFeatures().forEach((feature: Feature) => {
              if (feature.get('data').name === '风圈') {
                source?.removeFeature(feature)
              }
            })
          }
        })
        this._vectorSource?.addFeatures(features)
        this._vectorLayer?.setSource(this._vectorSource)
        // 清空html
        container.innerHTML = ''
        const content = document.createElement('div')
        content.className = 'popup-content'
        const closer = document.createElement('div')
        closer.className = 'popup-closer'
        closer.innerHTML = '<span>×</span>'
        container.appendChild(closer)
        container.appendChild(content)
        // 关闭popup
        closer.onclick = () => {
          popup.setPosition(undefined)
          closer.blur()
          return false
        }
        const geometry: Point = feature.getGeometry() as Point

        // 台风时间
        const typhoonTime = document.createElement('div')
        typhoonTime.className = 'typhoon-time'
        const time1 = time.split(' ')[1].split(':')
        typhoonTime.innerHTML = '[' + tyname + ']' + time.split(' ')[0] + ' ' + time1[0] + ':' + time1[1]
        container.insertAdjacentElement('afterbegin', typhoonTime)
        // 中心位置
        const typhoonCenter = document.createElement('div')
        typhoonCenter.className = 'typhoon-center'
        typhoonCenter.insertAdjacentHTML('afterbegin', '<span>中心位置:</span>')
        typhoonCenter.insertAdjacentHTML('beforeend', `<span>东经${lng}° 北纬${lat}°</span>`)
        content.appendChild(typhoonCenter)
        // 风速风力
        const typhoonSpeed = document.createElement('div')
        typhoonSpeed.className = 'typhoon-speed'
        typhoonSpeed.insertAdjacentHTML('afterbegin', '<span>风速风力:</span>')
        typhoonSpeed.insertAdjacentHTML('beforeend', `<span>${speed}米/秒 ${power}级(${strong?.split('(')[0] || '--'})</span>`)
        content.appendChild(typhoonSpeed)
        // 中心气压
        const typhoonPressure = document.createElement('div')
        typhoonPressure.className = 'typhoon-pressure'
        typhoonPressure.insertAdjacentHTML('afterbegin', '<span>中心气压:</span>')
        typhoonPressure.insertAdjacentHTML('beforeend', `<span>${pressure || '--'}百帕</span>`)
        content.appendChild(typhoonPressure)
        // 移动方向
        const typhoonDirection = document.createElement('div')
        typhoonDirection.className = 'typhoon-direction'
        typhoonDirection.insertAdjacentHTML('afterbegin', '<span>移动方向:</span>')
        typhoonDirection.insertAdjacentHTML('beforeend', `<span>${move_dir || '--'}</span>`)
        content.appendChild(typhoonDirection)
        // 弹出弹框
        popup.setPosition(geometry.getCoordinates())
      }
    })
    return popup
  }
  _styleFunction(feat) {
    const data = feat.get('data')
    const radius: number = data.radius
    const geomType = feat.getGeometry()?.getType()
    const isLive = data.isLive
    let pointColor = ''
    let lineColor = ''
    let fillColor = ''
    let lineWidth = isLive ? 2 : 1
    let lineDash = isLive ? [0, 0] : [4, 4]
    let styles = []
    if (geomType !== 'Polygon') {
      const strong = data.strong?.split('(')[0]
      pointColor = ColorDict[strong] || '#30fc31'
      lineColor = pointColor
      if (geomType === 'LineString' && !isLive) {
        lineColor = LineColorDict[data.sets]
      }
    } else {
      interface colorDict {
        [key: number]: string,
        7: string,
        10: string,
        12: string
      }
      const colorDict: colorDict = {
        7: '255, 0, 0',
        10: '155, 155, 0',
        12: '0, 0, 255'
      }
      lineWidth = 1
      lineDash = [0, 0]
      lineColor = `rgba(${colorDict[radius]}, 1)`
      fillColor = `rgba(${colorDict[radius]}, 0.1)`
    }

    styles.push(new Style({
      image: new Circle({
        radius: isLive ? 5 : 3,
        fill: new Fill({
          color: pointColor
        }),
        stroke: new Stroke({
          color: 'black',
          width: 1
        })
      }),
      stroke: new Stroke({
        color: lineColor,
        width: lineWidth,
        lineCap: 'square',
        lineDash: lineDash
      }),
      fill: new Fill({
        color: fillColor
      })
    }))
    return styles
  }
}
